/*
 * @Author: Colin Dai
 * @Date: 2022-05-10 17:23:02
 * @LastEditors: Colin Dai
 * @LastEditTime: 2022-05-10 19:53:24
 * @FilePath: /jy-uos/src/softbus/communication/udp/source/udp_discovery_service.c
 * @Description: 
 * dailianzheng@163.com
 * Copyright (c) 2022 by Colin.Dai, All Rights Reserved. 
 */

#include "udp_discovery_service.h"
#include "discovery_error.h"
#include "log.h"
#include "securec.h"
#include "nstackx_error.h"
#include "udp_socket.h"
#include "os_adapter.h"
#include "discovery_info.h"
#include <stdlib.h>

#include <pthread.h>

#define MIN_STACK_SIZE 0x8000

typedef void *(*Runnable)(void *argv);
typedef struct ThreadAttr ThreadAttr;
struct ThreadAttr {
    const char *name;
    uint32_t stackSize;
    uint8_t priority;
    uint8_t reserved1;
    uint16_t reserved2;
};



//全局变量
//服务端FD
int g_serverFd = -1;
int g_clientFd = -1;
intptr_t g_coapTaskId = -1;

unsigned int g_terminalFlag = 0;

//函数前置申明
int UdpInitSocket(void);
void UdpDeinitSocket(void);
int CreateUdpListenThread(void);
void UdpReadHandle(unsigned int uwParam1, unsigned int uwParam2, unsigned int uwParam3, unsigned int uwParam4);
int CreateThread(Runnable run, void *argv, const ThreadAttr *attr, unsigned int *threadId);


/**
 * @brief 创建线程
 * @param {Runnable} run
 * @param {void} *argv
 * @param {ThreadAttr} *attr
 * @param {unsigned int} *threadId
 * @return {*}
 */
int CreateThread(Runnable run, void *argv, const ThreadAttr *attr, unsigned int *threadId)
{
    pthread_attr_t threadAttr;
    pthread_attr_init(&threadAttr);
    pthread_attr_setstacksize(&threadAttr, (attr->stackSize | MIN_STACK_SIZE));
    struct sched_param sched = {attr->priority};
    pthread_attr_setschedparam(&threadAttr, &sched);
    int errCode = pthread_create((pthread_t *)threadId, &threadAttr, run, argv);
    return errCode;
}


/**
 * @brief 初始化UDP发现服务
 * @param {*}
 * @return {*}
 */
int UdpDiscoveryInit(void)
{
    SOFTBUS_PRINT("[UDP] UdpDiscoveryInit enter\n");

    int ret = NSTACKX_EOK;

    //初始化udp socket
    ret = UdpInitSocket();
    if (ret != NSTACKX_EOK) {
        SOFTBUS_PRINT("[UDP] Init socket fail\n");
        return ret;
    }

    //创建监听线程
    return CreateUdpListenThread();
}

/**
 * @brief 去初始化UDP发现服务
 * @param {*}
 * @return {*}
 */
int UdpDiscoveryDeinit(void)
{
    SOFTBUS_PRINT("[UDP] UdpDiscoveryDeinit enter\n");

    UdpDeinitSocket();

    return NSTACKX_EOK;

}

/**
 * @brief 初始化udp socket
 * @param {*}
 * @return {*}
 */
int UdpInitSocket(void)
{
    if (g_serverFd >= 0) {
        return NSTACKX_EOK;
    }
    struct sockaddr_in sockAddr;
    (void)memset_s(&sockAddr, sizeof(sockAddr), 0, sizeof(sockAddr));
    sockAddr.sin_port = htons(UDP_DISCOVERY_DEFAULT_PORT);
    g_serverFd = CreateUdpServer(&sockAddr);
    if (g_serverFd < 0) {
        return NSTACKX_OVERFLOW;
    }

    return NSTACKX_EOK;
}

/**
 * @brief 关闭UDP通道
 * @param {*}
 * @return {*}
 */
void UdpDeinitSocket(void)
{
    CloseSocket(&g_serverFd);
    CloseSocket(&g_clientFd);
    SOFTBUS_PRINT("[UDP] UdpDeinitSocket:%d\n", g_serverFd);
    return;
}


static void HandleReadEvent(int fd)
{
    int socketFd = fd;

    SOFTBUS_PRINT("[UDP] HandleReadEvent begin\n");
   
    unsigned char *recvBuffer = calloc(1, MAX_PDU_SIZE + 1);
    if (recvBuffer == NULL) {
        return;
    }
    ssize_t nRead;
    nRead = UdpSocketRecv(socketFd, recvBuffer, MAX_PDU_SIZE);
    if ((nRead == 0) || (nRead < 0 && errno != EAGAIN &&
        errno != EWOULDBLOCK && errno != EINTR)) {
        free(recvBuffer);
        return;
    }   
    
    //TODO：解析设备发现报文，并回应信息
    /*
    COAP_Packet decodePacket;
    (void)memset_s(&decodePacket, sizeof(COAP_Packet), 0, sizeof(COAP_Packet));
    decodePacket.protocol = COAP_UDP;
    COAP_SoftBusDecode(&decodePacket, recvBuffer, nRead);
    PostServiceDiscover(&decodePacket);
    */

    SOFTBUS_PRINT("[UDP] HandleReadEvent %s\n",recvBuffer);


    free(recvBuffer);
  
}


void UdpReadHandle(unsigned int uwParam1, unsigned int uwParam2, unsigned int uwParam3, unsigned int uwParam4)
{
    (void)uwParam1;
    (void)uwParam2;
    (void)uwParam3;
    (void)uwParam4;
    int ret;
    fd_set readSet;
    int serverFd = g_serverFd;
    SOFTBUS_PRINT("[UDP] UdpReadHandle coin select begin\n");
    while (g_terminalFlag) {
        FD_ZERO(&readSet);
        FD_SET(serverFd, &readSet);
        ret = select(serverFd + 1, &readSet, NULL, NULL, NULL);
        if (ret > 0) {
            if (FD_ISSET(serverFd, &readSet)) {
                HandleReadEvent(serverFd);
            }
        } else {
            SOFTBUS_PRINT("[UDP]ret:%d,error:%d\n", ret, errno);
        }
    }
    SOFTBUS_PRINT("[UDP] UdpReadHandle exit\n");
}

/**
 * @brief 创建UDP监听线程
 * @param {*}
 * @return {*}
 */
int CreateUdpListenThread(void)
{   
    
    g_terminalFlag = 1;

    if (g_coapTaskId != -1) {
        return NSTACKX_EOK;
    }

    ThreadAttr attr = {"udp_listen_task", 0x800, 20, 0, 0};
    int error = CreateThread((Runnable)UdpReadHandle, NULL, &attr, (unsigned int *)&g_coapTaskId);
    if (error != 0) {
        g_terminalFlag = 0;
        SOFTBUS_PRINT("[UDP] create task fail\n");
        return NSTACKX_EFAILED;
    }
    return NSTACKX_EOK;
}



